package com.tbit.uqbike.webmanager.util;

import com.tbit.uqbike.object.business.Point;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.Polygon;

import java.util.ArrayList;
import java.util.List;

/**
 * 电子围栏工具类
 *
 * @author Leon
 * 2017年11月30日 上午9:11:14
 */
public class GeoUtil {

    /**
     * 经纬度字符串转points
     *
     * @param pointString
     * @return
     */
    public static List<Coordinate> getCoordinate(String pointString) {
        List<Coordinate> coordinates = new ArrayList<Coordinate>();

        String[] pointArray = pointString.split(";");
        for (int i = 0; i < pointArray.length; i++) {
            String[] lonlat = pointArray[i].split(",");
            if (lonlat.length == 2) {
                coordinates.add(new Coordinate(Double.parseDouble(lonlat[0]), Double.parseDouble(lonlat[1])));
            }
        }
        String[] lonlat = pointArray[0].split(",");
        coordinates.add(new Coordinate(Double.parseDouble(lonlat[0]), Double.parseDouble(lonlat[1])));
        return coordinates;
    }

    /**
     * 经纬度字符串转points
     *
     * @param pointString
     * @return
     */
    public static List<Point> getPoints(String pointString) {
        List<Point> points = new ArrayList<Point>();

        String[] pointArray = pointString.split(";");
        for (int i = 0; i < pointArray.length; i++) {
            String[] lonlat = pointArray[i].split(",");
            if (lonlat.length == 2) {
                points.add(new Point(Double.parseDouble(lonlat[0]), Double.parseDouble(lonlat[1])));
            }
        }

        return points;
    }

    /**
     * Points转字符串
     *
     * @param points
     * @return
     */
    public static String getPointToString(List<Point> points) {
        StringBuilder result = new StringBuilder();
        int size = points.size();
        for (int i = 0; i < size; i++) {
            Point point = points.get(i);
            result.append(String.valueOf(point.getX())).append(",").append(String.valueOf(point.getY()));

            if (i != (size - 1)) {
                result.append(";");
            }
        }

        return result.toString();
    }

    /**
     * 获取电子围栏的中心
     *
     * @param points
     * @return
     */
    public static Point GetCenterPoint(List<Point> points) {
        // 以下为简化方法（400km以内）
        int total = points.size();
        double lat = 0, lon = 0;
        for (Point p : points) {
            lat += p.getX() * Math.PI / 180;
            lon += p.getY() * Math.PI / 180;
        }
        lat /= total;
        lon /= total;
        return new Point(lat * 180 / Math.PI, lon * 180 / Math.PI);
    }

    /**
     * 获取电子围栏的重心点
     *
     * @param mPoints
     * @return
     */
    public static Point getCenterOfGravityPoint(List<Point> mPoints) {
        double area = 0.0;//多边形面积
        double Gx = 0.0, Gy = 0.0;// 重心的x、y
        for (int i = 1; i <= mPoints.size(); i++) {
            double iLat = mPoints.get(i % mPoints.size()).getX();
            double iLng = mPoints.get(i % mPoints.size()).getY();
            double nextLat = mPoints.get(i - 1).getX();
            double nextLng = mPoints.get(i - 1).getY();
            double temp = (iLat * nextLng - iLng * nextLat) / 2.0;
            area += temp;
            Gx += temp * (iLat + nextLat) / 3.0;
            Gy += temp * (iLng + nextLng) / 3.0;
        }
        Gx = Gx / area;
        Gy = Gy / area;
        Double X = Double.valueOf(String.valueOf(Gx).substring(0, String.valueOf(Gx).indexOf(".") + 7));
        Double Y = Double.valueOf(String.valueOf(Gy).substring(0, String.valueOf(Gy).indexOf(".") + 7));
        return new Point(X, Y);
    }

    /**
     * 获取电子围栏的内心点
     *
     * @param CoordinateList
     * @return
     */
    public static Point getCenterOfGravityPoint4(List<Coordinate> CoordinateList) {
        Coordinate[] coordinates = new Coordinate[CoordinateList.size()];
        CoordinateList.toArray(coordinates);
        GeometryFactory geometryFactory = new GeometryFactory();
        Polygon p = geometryFactory.createPolygon(coordinates);//创建多边形
        org.locationtech.jts.geom.Point pt = p.getInteriorPoint();//多边形内心
        return new Point(pt.getX(), pt.getY());
    }

    /***
     * 获取点到多边形的距离
     * @param point
     * @param points
     * @return
     */
    public static Double getDistance(Point point, List<Point> points) {
        /** 最近的边 */
        Double distanceShort = null;
        Integer len = points.size();
        Integer maxIndex = len - 1;
        for (int i = 0; i < len; i++) {
            // 多边形中当前点
            Point currentPoint = points.get(i);
            Point nearPoint = maxIndex == i ? points.get(0) : points.get(i + 1);
            double a, b, c;
            a = getDistance(point, currentPoint);// 点与多边形一点的距离
            b = getDistance(point, nearPoint);// 点与多边形一点的距离
            c = getDistance(currentPoint, nearPoint);// 多边形邻边距离
            if (a * a >= b * b + c * c) {
                /** a为斜边 */
                if (distanceShort == null || distanceShort > b) {
                    distanceShort = b;
                }

                continue;
            }
            if (b * b >= c * c + a * a) {
                /** b为斜边 */
                if (distanceShort == null || distanceShort > a) {
                    distanceShort = a;
                }

                continue;
            }

            double l = (a + b + c) / 2; // 周长的一半
            double s = Math.sqrt(l * (l - a) * (l - b) * (l - c)); // 海伦公式求面积
            Double ss = 2 * s / c;
            if (distanceShort == null) {
                /** c为斜边，最近距离 */
                distanceShort = ss;
            }
        }
        return distanceShort;
    }

    /**
     * 检测点是否在多边形内
     *
     * @param point
     * @param points
     * @return
     */
    public static boolean IsPtInPoly(Point point, List<Point> points) {
        boolean flag = false;
        int n = points.size();
        Point dp = point;
        Point[] dps = new Point[n];
        points.toArray(dps);
        for (int i = 0; i < n; i++) {
            if (dp.getY() < dps[i].getY() && dp.getY() < dps[(i + 1) % n].getY()) {
                continue;
            }
            if (dps[i].getX() <= dp.getX() && dps[(i + 1) % n].getX() <= dp.getX()) {
                continue;
            }
            double dx = dps[(i + 1) % n].getX() - dps[i].getX();
            double dy = dps[(i + 1) % n].getY() - dps[i].getY();
            double t = (dp.getX() - dps[i].getX()) / dx;
            double y = t * dy + dps[i].getY();
            if (y <= dp.getY() && t >= 0 && t <= 1) {
                flag = !flag;
            }
        }
        return flag;
    }

    /**
     * 获取两点之间的距离
     */
    public static double getDistance(Point p1, Point p2) {
        double lon1 = (Math.PI / 180) * p1.getX();
        double lon2 = (Math.PI / 180) * p2.getX();
        double lat1 = (Math.PI / 180) * p1.getY();
        double lat2 = (Math.PI / 180) * p2.getY();

        // 地球半径
        double R = 6371;
        double d = Math.acos(Math.sin(lat1) * Math.sin(lat2) + Math.cos(lat1) * Math.cos(lat2) * Math.cos(lon2 - lon1)) * R;
        // 转换成千米（根据自己需求）

        return d * 1000;
    }

    public static void main(String[] args) {

        List<Coordinate> coordinates = getCoordinate("113.949139,22.550386;113.949029,22.550373;113.949054,22.549786;113.950467,22.549799;113.950452,22.549857;113.949137,22.549865");
        List<Point> sm = getPoints("113.949139,22.550386;113.949029,22.550373;113.949054,22.549786;113.950467,22.549799;113.950452,22.549857;113.949137,22.549865");
        System.out.println("coordinates: " + coordinates);


        Point zxPoint = GeoUtil.GetCenterPoint(sm);
        Point newPoint = GeoUtil.getCenterOfGravityPoint(sm);
        Point pt = getCenterOfGravityPoint4(coordinates);

        System.out.println("区域经纬度集合: " + sm);
        System.out.println("区域中心点: " + zxPoint);
        System.out.println("区域重心点: " + newPoint);
        System.out.println("区域内心点：" + pt);
        System.out.println("中心结果： " + IsPtInPoly(zxPoint, sm));
        System.out.println("重心结果： " + IsPtInPoly(newPoint, sm));
        System.out.println("内心结果： " + IsPtInPoly(pt, sm));

        List<Point> points = getPoints("112.544931,27.740513;112.545933,27.739688;112.544254,27.737973;112.546858,27.736459;112.545954,27.735388;112.546927,27.734728;112.547797,27.734261;112.548691,27.733667;112.552115,27.72946;112.550184,27.72822;112.551369,27.726411;112.552988,27.725048;112.551914,27.723176;112.551441,27.722218;112.550751,27.721552;112.548853,27.721565;112.547399,27.721157;112.54688,27.720778;112.545824,27.719465;112.544476,27.718508;112.541025,27.720632;112.537775,27.723096;112.539251,27.724591;112.538612,27.725026;112.538252,27.725237;112.536806,27.726263;112.535733,27.725095;112.535357,27.725045;112.534981,27.7251;112.534295,27.725503;112.533398,27.724177;112.533966,27.723558;112.532594,27.722133;112.531728,27.722428;112.53065,27.721528;112.531667,27.720024;112.52942,27.71925;112.528558,27.719175;112.527736,27.719308;112.526717,27.719408;112.526029,27.719305;112.525482,27.719394;112.524891,27.719181;112.524468,27.718595;112.523635,27.717896;112.522801,27.71829;112.522109,27.718695;112.521615,27.719238;112.522718,27.720962;112.523001,27.722255;112.522207,27.722764;112.521941,27.722984;112.521243,27.723603;112.520828,27.723961;112.519833,27.725084;112.518964,27.724584;112.518369,27.724369;112.517579,27.724075;112.516207,27.726073;112.515309,27.728265;112.514455,27.72958;112.512056,27.730799;112.511524,27.732896;112.512063,27.734733;112.51015,27.73634;112.504409,27.738656;112.504658,27.742324;112.505829,27.742769;112.50646,27.743026;112.507555,27.743616;112.508652,27.744314;112.509761,27.744954;112.510094,27.74567;112.510811,27.746045;112.512436,27.746398;112.513905,27.746699;112.513595,27.747547;112.513227,27.747789;112.511347,27.748952;112.510889,27.748737;112.51029,27.748304;112.509765,27.748219;112.50938,27.748411;112.509016,27.749011;112.509136,27.74966;112.50976,27.750449;112.511003,27.751365;112.502188,27.762386;112.506853,27.764567;112.510402,27.766057;112.512651,27.76671;112.512267,27.76742;112.51194,27.767901;112.5115,27.767628;112.510816,27.768691;112.510419,27.769634;112.511383,27.769998;112.512353,27.77034;112.512798,27.770482;112.513291,27.769707;112.513424,27.769468;112.513856,27.768811;112.513552,27.768615;112.514304,27.76727;112.522203,27.769712;112.524898,27.766165;112.527591,27.762577;112.528181,27.761691;112.528656,27.761297;112.529188,27.760693;112.529193,27.75921;112.52954,27.757648;112.530361,27.754758;112.531364,27.749963;112.532395,27.750163;112.533149,27.748459;112.534259,27.748829;112.535123,27.749189;112.535925,27.74831;112.536144,27.747724;112.537402,27.746491;112.537707,27.746791;112.538285,27.746462;112.539166,27.747102;112.54088,27.745924;112.539983,27.743484;112.536029,27.738269;112.533416,27.736954;112.532723,27.736484;112.5326,27.736587;112.532242,27.736232;112.532951,27.73562;112.531055,27.73389;112.531618,27.733358;112.531921,27.732979;112.53218,27.732241;112.532551,27.731909;112.534666,27.736799;112.535823,27.738449;112.53937,27.743056;112.541305,27.742983;112.542889,27.741895");
        Point point = new Point(112.509215, 27.74409);
        System.out.println(IsPtInPoly(point, points));
        System.out.println(getDistance(point, points));

    }
}
